/*****************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 */

//~#include "test.h"

#include <sys/types.h>
#include <stdbool.h>
#include <string.h>

#include "testutil.h"
//~#include "memdebug.h"

#include <curl/curl.h>

#include "mylog.h"

#define MAIN_LOOP_HANG_TIMEOUT     90 * 1000
#define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000

/*
 * Source code in here hugely as reported in bug report 651464 by
 * Christopher R. Palmer.
 *
 * Use multi interface to get document over proxy with bad port number.
 * This caused the interface to "hang" in libcurl 7.10.2.
 */
int test(const char *URL) {
	CURL *c;
	int res = 0;
	CURLM *m = NULL;
	fd_set rd, wr, exc;
	CURLMcode ret;
	char done = false;
	int running;
	int max_fd;
	int rc;
	struct timeval ml_start;
	struct timeval mp_start;
	char ml_timedout = false;
	char mp_timedout = false;

	if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
		fprintf(stderr, "curl_global_init() failed\n");
		LOGD("curl_global_init() failed\n");
		//~return TEST_ERR_MAJOR_BAD;
		return 1;
	}

	if ((c = curl_easy_init()) == NULL) {
		fprintf(stderr, "curl_easy_init() failed\n");
		LOGD("curl_easy_init() failed\n");
		curl_global_cleanup();
		//~return TEST_ERR_MAJOR_BAD;
		return 1;
	}

	/* The point here is that there must not be anything running on the given
	 proxy port */
#if 0
	if (libtest_arg2)
	test_setopt(c, CURLOPT_PROXY, libtest_arg2);
	test_setopt(c, CURLOPT_URL, URL);
	test_setopt(c, CURLOPT_VERBOSE, 1L);
#endif

	if ((m = curl_multi_init()) == NULL) {
		fprintf(stderr, "curl_multi_init() failed\n");
		LOGD("curl_multi_init() failed\n");
		curl_easy_cleanup(c);
		curl_global_cleanup();
		//~return TEST_ERR_MAJOR_BAD;
		return 1;
	}

	if ((ret = curl_multi_add_handle(m, c)) != CURLM_OK) {
		fprintf(stderr, "curl_multi_add_handle() failed, "
			"with code %d\n", ret);
		LOGD("curl_multi_add_handle() failed, with code %d\n", ret);
		curl_multi_cleanup(m);
		curl_easy_cleanup(c);
		curl_global_cleanup();
		//~return TEST_ERR_MAJOR_BAD;
		return 1;
	}

	ml_timedout = false;
	ml_start = tutil_tvnow();

	while (!done) {
		struct timeval interval;

		interval.tv_sec = 1;
		interval.tv_usec = 0;

		if (tutil_tvdiff(tutil_tvnow(), ml_start) > MAIN_LOOP_HANG_TIMEOUT) {
			ml_timedout = true;
			break;
		}
		mp_timedout = false;
		mp_start = tutil_tvnow();

		fprintf(stderr, "curl_multi_perform()\n");
		LOGD("curl_multi_perform()\n");

		ret = CURLM_CALL_MULTI_PERFORM;

		while (ret == CURLM_CALL_MULTI_PERFORM) {
			ret = curl_multi_perform(m, &running);
			if (tutil_tvdiff(tutil_tvnow(), mp_start)
					> MULTI_PERFORM_HANG_TIMEOUT) {
				mp_timedout = true;
				break;
			}
		}
		if (mp_timedout)
			break;

		if (!running) {
			/* This is where this code is expected to reach */
			int numleft;
			CURLMsg *msg = curl_multi_info_read(m, &numleft);
			fprintf(stderr, "Expected: not running\n");
			LOGD("Expected: not running\n");
			LOGD("msg=%p, numleft=%d", msg, numleft);
			if (msg && !numleft)
				res = 100; /* this is where we should be */
			else
				res = 99; /* not correct */
			break;
		}
		fprintf(stderr, "running == %d, ret == %d\n", running, ret);
		LOGD("running == %d, ret == %d\n", running, ret);

		if (ret != CURLM_OK) {
			res = 2;
			break;
		}

		FD_ZERO(&rd);
		FD_ZERO(&wr);
		FD_ZERO(&exc);
		max_fd = 0;

		fprintf(stderr, "curl_multi_fdset()\n");
		LOGD("curl_multi_fdset()\n");
		if (curl_multi_fdset(m, &rd, &wr, &exc, &max_fd) != CURLM_OK) {
			fprintf(stderr, "unexpected failured of fdset.\n");
			LOGD("unexpected failured of fdset.\n");
			res = 3;
			break;
		}
		rc = select(max_fd + 1, &rd, &wr, &exc, &interval);
		fprintf(stderr, "select returned %d\n", rc);
		LOGD("select returned %d\n", rc);
	}

	if (ml_timedout || mp_timedout) {
		if (ml_timedout)
		{
			fprintf(stderr, "ml_timedout\n");
			LOGD("ml_timedout\n");
		}
		if (mp_timedout)
		{
			fprintf(stderr, "mp_timedout\n");
			LOGD("mp_timedout\n");
		}
		fprintf(stderr, "ABORTING TEST, since it seems "
			"that it would have run forever.\n");
		LOGD("ABORTING TEST, since it seems "
			"that it would have run forever.\n");
		//~res = TEST_ERR_RUNS_FOREVER;
		return 1;
	}

	test_cleanup:

	if (m) {
		curl_multi_remove_handle(m, c);
		curl_multi_cleanup(m);
	}
	curl_easy_cleanup(c);
	curl_global_cleanup();

	return res;
}

